<?php

namespace App\Http\Controllers\Admin;

use App\Exports\AgentExport;
use App\Exports\AgentTeamListExport;
use App\Exports\WithdrawRecordExport;
use App\Http\Controllers\Controller;
use App\Jobs\AdjustAgentAndSubParentPathJob;
use App\Models\Agent;
use App\Models\AgentRevenue;
use App\Models\AgentWithdrawRecord;
use App\Models\DailyReportStatistics;
use App\Models\IndicatorsDailySummary;
use App\Services\CacheLockKeyConst;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

class AgentController extends Controller
{
    public function store(Request $request)
    {
        $agent = null;
        try {
            // 解决 firstOrCreate 并发问题, 先创建
            /** @var DailyReportStatistics $dailyReportStatistics */
            $dailyReportStatistics = CacheLockKeyConst::dailyReportStatistics(\Carbon\Carbon::today());

            DB::beginTransaction();

            $agent = Agent::query()->create(tap(
                $request->only([
                    'name', 'password', 'level', 'cultivation_award',
                    'affiliated_partner', // 所属合伙人
                    'real_name', 'gender', 'phone', 'wechat', 'birthday', 'province', 'city', 'area', 'address',
                ]),
                function (&$values) {
                    $values = array_filter($values); // 过滤空值,比如空的合伙人传值
                    $values['password'] = Hash::make($values['password']);
                    $values['invitation_code'] = Agent::generateInvitationCode();
                }
            ));

            // 代理标签
            $agent->labels()->sync($request->get('labels'));

            // 累加新增代理人数
            $dailyReportStatistics->incrementOrDecrementAgent();

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            $this->response->errorBadRequest($e->getMessage());
        }

        activity()
            ->on($agent)
            ->by(\Auth::user())
            ->withProperty('ip', app('request')->ip())
            ->log(sprintf('创建代理商: %s (%s)', $agent['name'] ?? '', $agent['phone'] ?? ''));

        return $this->response->created(null, $agent);
    }

    public function update(Request $request, $id)
    {
        $agentUnique = null;
        try {
            $agentUnique = Agent::query()->where('name', $request->get('name'))->firstOrFail();
        } catch (\Exception $e) {
            // 名字不存在直接 无视
        }
        if (((int) ($agentUnique['id'] ?? 0) !== (int) $id) && ($agentUnique['name'] ?? '') === $request->get('name')) {
            $this->response->errorBadRequest('该代理名称已注册');
        }

        $agent = null;
        try {
            DB::beginTransaction();

            $agent = Agent::query()
                ->with(['partnerIdentity'])
                ->findOrFail($id);

            $agent->update(tap(
                array_filter($request->only([
                    'name', 'password', 'level', 'cultivation_award', 'affiliated_partner',
                    'real_name', 'gender', 'wechat', 'birthday', 'province', 'city', 'area', 'address',
                ])),
                function (&$values) use (&$agent) {
                    // 当代理商有合伙人身份时 不能编辑所属合伙人
                    if ($agent->partnerIdentity !== null) {
                        unset($values['affiliated_partner']);
                    }

                    isset($values['password']) && ($values['password'] = Hash::make($values['password']));
                }
            ));

            // 当前代理商没有合伙人身份, 并且编辑所属合伙人, 同步下级
            if ($agent->partnerIdentity === null && $agent->wasChanged(['affiliated_partner'])) {
                $changeFun = function ($agentIds) use (&$changeFun, &$agent) {
                    $subordinateAgentIds = Agent::query()
                        ->whereIn('direct_inviter', $agentIds)
                        ->whereDoesntHave('partnerIdentity')
                        ->pluck('id');
                    if ($subordinateAgentIds->isEmpty()) {
                        return;
                    }
                    Agent::query()
                        ->whereIn('id', $subordinateAgentIds)
                        ->update(['affiliated_partner' => $agent['affiliated_partner']]);
                    $changeFun($subordinateAgentIds);
                };
                $changeFun([$agent['id']]);
            }

            // 代理标签
            $agent->labels()->sync($request->get('labels'));

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            $this->response->errorBadRequest($e->getMessage());
        }

//        if ($agent->wasChanged()) {
//            $changesMap = [
//                'name' => '代理名称', 'level' => '等级',
//                'cultivation_award' => '培养奖', 'real_name' => '代理姓名', 'gender' => '性别',
//                'wechat_number' => '微信号', 'birthday' => '生日', ''
//            ];
//            $valueMap = [
//                'level' => array_combine(array_keys(Agent::LEVEL_MAP), array_column(array_values(Agent::LEVEL_MAP), 'text')),
//                'cultivation_award' => Agent::CULTIVATION_AWARD_MAP,
//            ];
//            $changes = [];
//            foreach (Arr::only($agent->getChanges(), array_keys($changesMap)) as $field => $value) {
//                $changes[] = [
//                    'field' => $changesMap[$field],
//                    'text' =>
//                        (isset($valueMap[$field]) ? $valueMap[$field][$agent->getOriginal($field)] ?? '' : $agent->getOriginal($field)) . ' 改为 ' .
//                        (isset($valueMap[$field]) ? $valueMap[$field][$value] : '')
//                ];
//            }
//            if (!empty($changes)) {
                activity()
                    ->on($agent)
                    ->by(\Auth::user())
                    ->withProperty('ip', app('request')->ip())
//                    ->log(sprintf('编辑代理商: %s', array_reduce($changes, fn($init, $item) => $init . ($item['field'] . ' ' . $item['text']) . '; ', '')));
                    ->log(sprintf('编辑代理商: %s (%s)', $agent['name'] ?? '', $agent['id'] ?? ''));
//            }
//        }

        return $this->response->created(null, $agent);
    }

    public function updateStatus(Request $request, $id)
    {
        $agent = null;
        try {
            $agent = Agent::query()->findOrFail($id);
            $agent->update(['status' => $request->get('status')]);
        } catch (\Exception $e) {
            $this->response->errorBadRequest($e->getMessage());
        }

        activity()
            ->on($agent)
            ->by(\Auth::user())
            ->withProperty('ip', app('request')->ip())
            ->log(sprintf('%s代理商: %s (%s)', Agent::STATUS_MAP[$request->get('status')] ?? '', $agent['name'] ?? '', $agent['phone'] ?? ''));

        return $this->response->created(null, $agent);
    }

    public function list(Request $request)
    {
        if ($request->has('export')) {
            return (new AgentExport($request))->download(sprintf('代理商(%s).xls', Carbon::now()->format('YmdHisu')));
        }

        $paginate = Agent::query()
            ->with(['affiliatedPartner.partnerIdentity', 'directInviter', 'indirectInviter'])
            ->when($request->get('with_labels', true), function (Builder $builder) {
                $builder->with(['labels']);
            })
            ->when($request->get('name'), function (Builder $builder, $name) {
                $builder->where('agents.name', 'like', "%$name%");
            })
            ->when($request->get('phone'), function (Builder $builder, $phone) {
                $builder->where('agents.phone', 'like', "%$phone%");
            })
            ->when($request->get('level'), function (Builder $builder, $level) {
                $builder->where('agents.level', $level);
            })
            ->when($request->get('labels'), function (Builder $builder, $labels) {
                $builder->whereHas('labels', function (Builder $subBuilder) use ($labels) {
                    $subBuilder->whereIn('agent_labels.id', $labels);
                });
            })
            ->when($request->get('affiliated_partner'), function (Builder $builder, $affiliatedPartner) {
                $builder->where('agents.affiliated_partner', $affiliatedPartner);
            })
            ->when($request->get('affiliated_partner_phone'), function (Builder $builder, $affiliatedPartnerPhone) {
                $builder->join('agents as agents_self', 'agents_self.id', '=', 'agents.affiliated_partner')
                    ->where('agents_self.phone', 'like', "%$affiliatedPartnerPhone%");
            })
            ->when($request->get('direct_inviter_phone'), function (Builder $builder, $directInviterPhone) {
                $builder->join('agents as agents_self', 'agents_self.id', '=', 'agents.direct_inviter')
                    ->where('agents_self.phone', 'like', "%$directInviterPhone%");
            })
            ->when($request->get('indirect_inviter_phone'), function (Builder $builder, $indirectInviterPhone) {
                $builder->join('agents as agents_self', 'agents_self.id', '=', 'agents.indirect_inviter')
                    ->where('agents_self.phone', 'like', "%$indirectInviterPhone%");
            })
            ->when($request->get('created_at_begin'), function (Builder $builder, $createdAtBegin) {
                $builder->where('agents.created_at', '>=', $createdAtBegin);
            })
            ->when($request->get('created_at_end'), function (Builder $builder, $createdAtEnd) {
                $builder->where('agents.created_at', '<=', Carbon::parse($createdAtEnd)->endOfDay());
            })
            ->when(
                $request->get('performance_range_begin') && $request->get('performance_range_end'),
                function (Builder $builder) use ($request) {
                    $begin = $request->get('performance_range_begin');
                    $end = $request->get('performance_range_end');

                    $builder
                        ->select([
                            // 这里把 personal_performance 和 team_performance 去掉, 单独另外统计
                            'agents.id', 'agents.name', 'agents.phone', 'agents.level', 'agents.status', 'agents.created_at',
                            'agents.number_of_direct_inviter', 'agents.direct_inviter', 'agents.balance', 'agents.unextractable_balance',
                            'agents.number_of_direct_inviter', 'agents.number_of_direct_inviter', 'agents.number_of_indirect_inviter',
                            'agents.affiliated_partner', 'agents.login_frequency', 'agents.last_time_of_login',
                        ])
                        ->selectSub(
                        // 个人业绩按日期范围汇总
                            \DB::table('indicators_daily_summary')
                                ->selectRaw('sum(performance_summary)')
                                ->whereBetween('day', [$begin, $end])
                                ->where('type', IndicatorsDailySummary::TYPE_AGENT)
                                ->whereRaw('agent_id = agents.id'),
                            'personal_performance'
                        )
                        ->selectSub(
                        // 团队业绩按日期范围汇总
                            \DB::table('indicators_daily_summary')
                                ->selectRaw('sum(performance_summary)')
                                ->whereBetween('day', [$begin, $end])
                                ->where('type', IndicatorsDailySummary::TYPE_TEAM)
                                ->whereRaw('agent_id = agents.id'),
                            'team_performance'
                        );
                },
                function (Builder $builder) {
                    $builder->select('agents.*');
                })
            ->when($request->get('select'), function (Builder $builder, $select) {
                $builder->select($select);
            })
            ->paginate();

        return $this->response->array(
            $paginate->setCollection(
                $paginate->getCollection()->each(function (Agent &$agent) {
                    // 显示团队大咖
                    $agent['team_leader'] = '省到家团队';
                    // 1. 直接/间接 邀请 是否有大咖
                    ($directInviter = $agent->getRelationValue('directInviter')) && $directInviter = $directInviter->toArray();
                    ($indirectInviter = $agent->getRelationValue('indirectInviter')) && $indirectInviter = $indirectInviter->toArray();
                    $directInviterIsStrongMan = $directInviter !== null && ($directInviter['level'] ?? 0) === Agent::LEVEL_STRONGMAN;
                    $indirectInviterIsStrongMan = $indirectInviter !== null && ($indirectInviter['level'] ?? 0) === Agent::LEVEL_STRONGMAN;
                    if ($directInviterIsStrongMan || $indirectInviterIsStrongMan) {
                        if ($directInviterIsStrongMan) {
                            $agent['team_leader'] = $directInviter['name'] . '团队';
                        } elseif ($indirectInviterIsStrongMan) {
                            $agent['team_leader'] = $indirectInviter['name'] . '团队';
                        }
                    } else {
                        // 往上寻找合伙人
                        $parentIds = array_filter(explode('-', $agent['parent_path_id']));
                        if (!empty($parentIds)) {
                            $parents = arrayCombineByKey(
                                Agent::query()
                                    ->withoutGlobalScope('created_at')
                                    ->whereHas('partnerIdentity')
                                    ->whereIn('id', $parentIds)
                                    ->get()
                                    ->toArray(),
                                'id'
                            );
                            foreach (array_reverse($parentIds) as $parentId) {
                                if (isset($parents[$parentId])) {
                                    $agent['team_leader'] = $parents[$parentId]['name'] . '团队';
                                    break;
                                }
                            }
                        }
                    }
                })
            )
        );
    }

    public function info($id)
    {
        return $this->response->array(
            Agent::query()
                ->with(['labels', 'province', 'city', 'area', 'directInviter',
                    'indirectInviter', 'affiliatedPartner.partnerIdentity',
                    'promoted', 'partnerIdentity'])
                ->findOrFail($id)
                ->toArray()
        );
    }

    /**
     * 直接/间接 邀请人员
     * @param Request $request
     * @return mixed
     */
    public function teamList(Request $request)
    {
        if ($request->has('export')) {
            $agent = Agent::query()->findOrFail((int) $request->get('agent_id'));
            return (new AgentTeamListExport($request))->download(sprintf('%s(%s).xls', $agent['name'], $agent['phone']));
        }

        return $this->response->array(
        // 1 直接邀请 2 间接邀请
            Agent::query()
                ->when($request->get('type', 1), function (Builder $builder, $type) use ($request) {
                    if ((int) $type === 1) {
                        $builder->where('direct_inviter', $request->get('agent_id'));
                    } else {
                        $builder
                            ->with('directInviter')
                            ->where('indirect_inviter', $request->get('agent_id'));
                    }
                })
                ->when(
                    $request->get('performance_range_begin') && $request->get('performance_range_end'),
                    function (Builder $builder) use ($request) {
                        $begin = $request->get('performance_range_begin');
                        $end = $request->get('performance_range_end');

                        $builder
                            ->select([
                                // 这里把 personal_performance 和 team_performance 去掉, 单独另外统计
                                'agents.name', 'agents.phone', 'agents.level', 'agents.status', 'agents.created_at',
                                'agents.number_of_direct_inviter', 'agents.direct_inviter', 'agents.balance', 'agents.unextractable_balance',
                                'agents.number_of_direct_inviter', 'agents.number_of_direct_inviter', 'agents.number_of_indirect_inviter',
                                'agents.affiliated_partner', 'agents.login_frequency', 'agents.last_time_of_login',
                            ])
                            ->selectSub(
                            // 个人业绩按日期范围汇总
                                \DB::table('indicators_daily_summary')
                                    ->selectRaw('sum(performance_summary)')
                                    ->whereBetween('day', [$begin, $end])
                                    ->where('type', IndicatorsDailySummary::TYPE_AGENT)
                                    ->whereRaw('agent_id = agents.id'),
                                'personal_performance'
                            )
                            ->selectSub(
                            // 团队业绩按日期范围汇总
                                \DB::table('indicators_daily_summary')
                                    ->selectRaw('sum(performance_summary)')
                                    ->whereBetween('day', [$begin, $end])
                                    ->where('type', IndicatorsDailySummary::TYPE_TEAM)
                                    ->whereRaw('agent_id = agents.id'),
                                'team_performance'
                            );
                    },
                    function (Builder $builder) {
                        $builder->select('agents.*');
                    })
                ->paginate()
        );
    }

    /**
     * 调整余额
     * @param Request $request
     * @return \Dingo\Api\Http\Response
     * @throws \Throwable
     */
    public function adjustBalance(Request $request)
    {
        $this->validate($request, [
            'amount' => ['required', 'integer'],
            'balance_type' => 'required|in:balance,unextractable_balance',
        ], [
            'amount.required' => '缺失金额',
            'amount.integer' => '金额不合法',
            'balance_type.required' => '缺失金额类型',
            'balance_type.in' => '金额类型不合法',
        ]);

        try {
            DB::beginTransaction();

            $amount = $request->get('amount', 1); // 金额 (后端单位 分)
            $balanceType = $request->get('balance_type', 'unextractable_balance'); // 余额类型
            if ($agentId = $request->get('agent_id')) {
                $agent = Agent::query()->findOrFail($agentId);
            } elseif ($phone = $request->get('phone')) {
                $agent = Agent::query()->where('phone', $phone)->firstOrFail();
            } else {
                abort(400, '缺失用户标识');
            }
            if ($balanceType === 'balance') {
                $agent->incrementOrDecrementBalance(
                    abs($amount),
                    $amount >= 0 ? AgentRevenue::TYPE_ADMIN_BALANCE_INCREASE : AgentRevenue::TYPE_ADMIN_BALANCE_DECREASE,
                    null,
                    $amount >= 0 ? 'increment' : 'decrement'
                );
            } elseif ($balanceType === 'unextractable_balance') {
                $agent->incrementOrDecrementUnextractableBalance(
                    abs($amount),
                    $amount >= 0 ? AgentRevenue::TYPE_ADMIN_UNEXTRACTABLE_INCREASE : AgentRevenue::TYPE_ADMIN_UNEXTRACTABLE_DECREASE,
                    null,
                    $amount >= 0 ? 'increment' : 'decrement'
                );
            }

            DB::commit();
        } catch (ModelNotFoundException $e) {
            DB::rollBack();
            $this->response->errorBadRequest('用户不存在');
        } catch (\Exception $e) {
            DB::rollBack();
            $this->response->errorBadRequest($e->getMessage());
        }

        return $this->response->created(null);
    }

    /**
     * 流水明细
     */
    public function runningBill(Request $request)
    {
        return $this->response->array(
            AgentRevenue::query()
                ->without(['agent'])
                ->with(['source'])
                ->where('agent_id', $request->get('agent_id'))
                ->when($request->get('created_at_begin'), function (Builder $query, $createdAtBegin) {
                    $query->where('created_at', '>=', $createdAtBegin);
                })
                ->when($request->get('created_at_end'), function (Builder $query, $createdAtEnd) {
                    $query->where('created_at', '<=', Carbon::parse($createdAtEnd)->endOfDay());
                })
                ->paginate()
        );
    }

    /**
     * 提现操作列表
     * @param Request $request
     */
    public function withdrawalsRecord(Request $request)
    {
        if ($request->has('export')) {
            return (new WithdrawRecordExport($request))->download(sprintf('代理提现记录(%s).xls', Carbon::now()->format('YmdHisu')));
        }

        return $this->response->array(
            AgentWithdrawRecord::query()
                ->with(['agent'])
                ->where('agent_withdraw_records.status', $request->get('status', AgentWithdrawRecord::STATUS_PENDING_REVIEW))
                ->when($request->get('phone') || $request->get('name'), function (Builder $builder) use ($request) {
                    $builder
                        ->join('agents', 'agent_withdraw_records.agent_id', '=', 'agents.id')
                        ->when($request->get('phone'), function (Builder $builder, $phone) {
                            $builder->where('agents.phone', 'like', "%$phone%");
                        })
                        ->when($request->get('name'), function (Builder $builder, $name) {
                            $builder->where('agents.name', 'like', "%$name%");
                        });
                })
                ->when($request->get('created_at_begin'), function (Builder $builder, $createdAtBegin) {
                    $builder->where('agent_withdraw_records.created_at', '>=', $createdAtBegin);
                })
                ->when($request->get('created_at_end'), function (Builder $builder, $createdAtEnd) {
                    $builder->where('agent_withdraw_records.created_at', '<=', Carbon::parse($createdAtEnd)->endOfDay());
                })
                ->select(['agent_withdraw_records.*'])
                ->paginate()
        );
    }

    /**
     * 审批提现
     */
    public function approveWithdrawals(Request $request)
    {
        $this->validate($request, [
            'ids' => 'required',
            'status' => ['required', 'in:' . implode(',', [AgentWithdrawRecord::STATUS_PASSED, AgentWithdrawRecord::STATUS_DID_NOT_PASS])]
        ], [
            'ids.required' => 'ID 不能为空',
            'status.required' => '审批类型不能为空',
            'status.in' => '审批类型不合法'
        ]);

        try {
            \DB::beginTransaction();

            $records = AgentWithdrawRecord::query()
                ->with(['agent'])
                ->where('status', AgentWithdrawRecord::STATUS_PENDING_REVIEW)
                ->find($request->get('ids'));
            if ($records->isEmpty()) {
                $this->response->errorBadRequest('没有需要审核的提现');
            }

            foreach ($records as $item) {
                $item->agent->incrementOrDecrementAmountInWithdrawal($item['withdrawal_amount'], 'decrement'); // 减去提现中的金额

                if ($request->get('status') === AgentWithdrawRecord::STATUS_DID_NOT_PASS) {
                    $item->agent->incrementOrDecrementBalance($item['withdrawal_amount'], AgentRevenue::TYPE_EXTRACT_REFUSE, $item, 'increment');
                }
            }

            // 变更状态
            AgentWithdrawRecord::query()
                ->whereIn('id', $records->pluck('id'))
                ->update([
                    'audit_remarks' => $request->get('remark', ''),
                    'status' => $request->get('status'),
                    'operator_id' => \Auth::id()
                ]);

            \DB::commit();
        } catch (\Exception $e) {
            \DB::rollBack();
            $this->response->errorBadRequest($e->getMessage());
        }

        return $this->response->noContent();
    }

    /**
     * 改变代理所属层级关系
     */
    public function changeBelonging($id, Request $request)
    {
        validator(
            array_merge(['id' => (int) $id], $request->all()),
            [
                'id' => 'different:direct_inviter',
                'direct_inviter' => 'required|not_in:0'
            ],
            [
                'direct_inviter.required' => '请选择直接邀请人',
                'direct_inviter.not_in' => '请选择直接邀请人',
                'id.different' => '直接邀请人不能是自己'
            ]
        )->validate();

        try {
            $agent = Agent::query()->with(['directInviter:id', 'indirectInviter:id'])->findOrFail($id); // 要移动的用户
            $originDirectInviter = $agent->directInviter['id'] ?? null; // 原来的直接邀请人
            $originIndirectInviter = $agent->indirectInviter['id'] ?? null; // 原来的间接邀请人
        } catch (\Exception $e) {
            abort(400, '用户不存在');
        }
        try {
            $targetAgent = Agent::query()->findOrFail($request->get('direct_inviter')); // 移动到的目标用户
        } catch (\Exception $e) {
            abort(400, '目标用户不存在');
        }
        if (in_array((int) $id, [(int) $targetAgent['direct_inviter'], (int) $targetAgent['indirect_inviter']], true)) {
            abort(400, '要调整到的直接邀请人目标的直接邀请人或者间接邀请人不能是当前代理');
        }

        // 调整用户所属直接邀请和间接邀请人
        $agent->update([
            'direct_inviter' => $targetAgent->getKey(),
            'indirect_inviter' => $targetAgent['direct_inviter'] ?? 0 // 当前被调整人的间接邀请人是 目标直接邀请人的直接邀请人
        ]);

        // 调整当前用户和下级的 所属合伙人 以及 parent_path_id
        dispatch_now(new AdjustAgentAndSubParentPathJob($agent->getKey(), $originDirectInviter, $originIndirectInviter));

        return $this->response->noContent();
    }
}
